﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "DBCLoader.h"
#include <cstring>
#include <NodeCpp/Platform.h>
#if defined(PLATFORM_WINDOWS)
#pragma warning(disable:4996)
#endif

DBCLoader::DBCLoader(void)
    : RecordSize_(0)
    , RecordCount_(0)
    , FieldCount_(0)
    , StringSize_(0)
    , FieldsOffset_(NULL)
    , Data_(NULL)
    , StringTable_(NULL)
{
    Data_ = NULL;
    FieldsOffset_ = NULL;
}

DBCLoader::~DBCLoader(void)
{
    delete[] Data_;
    delete[] FieldsOffset_;
}

DBCLoader::Record DBCLoader::GetRecord(std::size_t _ID)
{
    NODECPP_ASSERT(Data_);
    return Record(*this, Data_ + _ID * RecordSize_);
}

std::uint32_t DBCLoader::GetFormatRecordSize(const char* _Fmt, std::int32_t* _IndexPos /* = NULL */)
{
	std::uint32_t _Recordsize = 0;
	std::int32_t _I = -1;
	for (std::uint32_t _X = 0; _Fmt[_X]; ++_X)
	{
		switch (_Fmt[_X])
		{
		case DBC_FF_FLOAT:
			_Recordsize += sizeof(float);
			break;
		case DBC_FF_INT:
			_Recordsize += sizeof(std::uint32_t);
			break;
		case DBC_FF_STRING:
			_Recordsize += sizeof(char*);
			break;
		case DBC_FF_SORT:
			_I = _X;
			break;
		case DBC_FF_IND:
			_I = _X;
			_Recordsize += sizeof(std::uint32_t);
			break;
		case DBC_FF_BYTE:
			_Recordsize += sizeof(std::uint8_t);
			break;
		case DBC_FF_LOGIC:
			NODECPP_ASSERT(false && "DBC files not have logic field type");
			break;
		case DBC_FF_NA:
		case DBC_FF_NA_BYTE:
			break;
		default:
			NODECPP_ASSERT(false && "unknown format character");
			break;
		}
	}

	if (_IndexPos) {
		*_IndexPos = _I;
	}

	return _Recordsize;
}

char* DBCLoader::AutoProduceData(const char* _Fmt, std::uint32_t& _Count, char**& _IndexTable)
{
	/*
	format STRING, NA, FLOAT,NA,INT <=>
	struct{
	char* field0,
	float field1,
	int field2
	}entry;

	this func will generate  entry[rows] data;
	*/

	typedef char* _Ptr;
	if (strlen(_Fmt) != FieldCount_) {
		return NULL;
	}

	// get struct size and index pos
	std::int32_t _I;
	std::uint32_t _Recordsize = GetFormatRecordSize(_Fmt, &_I);

	if (_I >= 0)
	{
		std::uint32_t _Maxi = 0;
		// find max index
		for (std::uint32_t _Y = 0; _Y < RecordCount_; ++_Y)
		{
			std::uint32_t _Ind = GetRecord(_Y).GetUInt(_I);
			if (_Ind > _Maxi) { _Maxi = _Ind; }
		}

		++_Maxi;
		_Count = _Maxi;
		_IndexTable = new _Ptr[_Maxi];
		memset(_IndexTable, 0, _Maxi * sizeof(_Ptr));
	}
	else
	{
		_Count = RecordCount_;
		_IndexTable = new _Ptr[RecordCount_];
	}

	char* _DataTable = new char[RecordCount_ * _Recordsize];

	std::uint32_t _Offset = 0;

	for (std::uint32_t _Y = 0; _Y < RecordCount_; ++_Y)
	{
		if (_I >= 0)
		{
			_IndexTable[GetRecord(_Y).GetUInt(_I)] = &_DataTable[_Offset];
		}
		else
		{
			_IndexTable[_Y] = &_DataTable[_Offset];
		}

		for (std::uint32_t _X = 0; _X < FieldCount_; ++_X)
		{
			switch (_Fmt[_X])
			{
			case DBC_FF_FLOAT:
				*((float*)(&_DataTable[_Offset])) = GetRecord(_Y).GetFloat(_X);
				_Offset += sizeof(float);
				break;
			case DBC_FF_IND:
			case DBC_FF_INT:
				*((std::uint32_t*)(&_DataTable[_Offset])) = GetRecord(_Y).GetUInt(_X);
				_Offset += sizeof(std::uint32_t);
				break;
			case DBC_FF_BYTE:
				*((std::uint8_t*)(&_DataTable[_Offset])) = GetRecord(_Y).GetUInt8(_X);
				_Offset += sizeof(std::uint8_t);
				break;
			case DBC_FF_STRING:
				*((char**)(&_DataTable[_Offset])) = NULL; // will be replaces non-empty or "" strings in AutoProduceStrings
				_Offset += sizeof(char*);
				break;
			case DBC_FF_LOGIC:
				NODECPP_ASSERT(false && "DBC files not have logic field type");
				break;
			case DBC_FF_NA:
			case DBC_FF_NA_BYTE:
			case DBC_FF_SORT:
				break;
			default:
				NODECPP_ASSERT(false && "unknown format character");
				break;
			}
		}
	}

	return _DataTable;
}

char* DBCLoader::AutoProduceStrings(const char* _Fmt, char* _DataTable)
{
	if (strlen(_Fmt) != FieldCount_) {
		return NULL;
	}

	char* _StringPool = new char[StringSize_];
	memcpy(_StringPool, StringTable_, StringSize_);

	std::uint32_t _Offset = 0;

	for (std::uint32_t _Y = 0; _Y < RecordCount_; ++_Y)
	{
		for (std::uint32_t _X = 0; _X < FieldCount_; ++_X)
		{
			switch (_Fmt[_X])
			{
			case DBC_FF_FLOAT:
				_Offset += sizeof(float);
				break;
			case DBC_FF_IND:
			case DBC_FF_INT:
				_Offset += sizeof(std::uint32_t);
				break;
			case DBC_FF_BYTE:
				_Offset += sizeof(std::uint8_t);
				break;
			case DBC_FF_STRING:
			{
				// fill only not filled entries
				char** _Slot = (char**)(&_DataTable[_Offset]);
				if (!*_Slot || !** _Slot)
				{
					const char* _St = GetRecord(_Y).GetString(_X);
					*_Slot = _StringPool + (_St - (const char*)StringTable_);
				}
				_Offset += sizeof(char*);
				break;
			}
			case DBC_FF_LOGIC:
				NODECPP_ASSERT(false && "DBC files not have logic field type");
				break;
			case DBC_FF_NA:
			case DBC_FF_NA_BYTE:
			case DBC_FF_SORT:
				break;
			default:
				NODECPP_ASSERT(false && "unknown format character");
				break;
			}
		}
	}

	return _StringPool;
}
